package com.hitqz.robot.driver.communication;

import cn.hutool.core.util.HexUtil;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.fazecast.jSerialComm.SerialPortMessageListener;
import com.hitqz.robot.common.core.util.SpringContextHolder;
import com.hitqz.robot.driver.dto.robot.SerialPortMessageDto;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Locale;

@Slf4j
public class SerialPortServer {

    private SerialPort serverSerialPort = null;

    private String comName;

    private SerialPort serialPort;

    /**
     * 绑定串口
     * @param comName 串口名称
     * @param baudRate 波特率
     * @param dataBits 数据位
     * @param stopBits 停止位
     * @param parity 校验位
     * @param messageDelimiter 消息分隔依据
     */
    public void bind(String serverName,String comName,int baudRate,int dataBits,int stopBits,int parity,byte[] messageDelimiter)  {

        try {
            HashMap<String, SerialPort> portMap = new HashMap<>(4);
            SerialPort[] commPorts = SerialPort.getCommPorts();
            log.info("[jSerialComm] available serial port count:{}", commPorts.length);
            for (SerialPort commPort : commPorts) {
                portMap.put(commPort.getSystemPortName(), commPort);
                log.info("[jSerialComm] available serial port:{},{}", commPort.getSystemPortName(),commPort.getPortDescription());
            }
            if (portMap.containsKey(comName)) {
                // 监听串口
                SerialPort comPort = portMap.get(comName);
                this.serverSerialPort = comPort;
                this.comName = comName;
                comPort.setBaudRate(baudRate);
                comPort.setNumDataBits(dataBits);
                comPort.setNumStopBits(stopBits);
                comPort.setParity(parity);
                if (comPort.openPort()) {
                    this.serialPort = comPort;
                    log.info("[jSerialComm] serial port {}  open success", comName);
                } else {
                    log.info("[jSerialComm] serial port {}  open failed,please check device", comName);
                }
                comPort.addDataListener(new SerialPortMessageListener() {
                    @Override
                    public int getListeningEvents() {
                        return SerialPort.LISTENING_EVENT_DATA_RECEIVED  | SerialPort.LISTENING_EVENT_PORT_DISCONNECTED | SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
                    }
                    //按照FFAA分割
                    @Override
                    public byte[] getMessageDelimiter() { return messageDelimiter; }

                    @Override
                    public boolean delimiterIndicatesEndOfMessage() { return false; }
                    @Override
                    public void serialEvent(SerialPortEvent event) {
                        int eventType = event.getEventType();
                        switch (eventType){
                            case SerialPort.LISTENING_EVENT_DATA_RECEIVED:
                                try {
                                    byte[] newData = event.getReceivedData();
                                    int numRead = newData.length;
//                                    byte[] newData = new byte[comPort.bytesAvailable()];
//                                    int numRead = comPort.readBytes(newData, newData.length);
                                    log.debug("[jSerialComm-{}]{} read {} bytes.",serverName, comName, numRead);
                                    String hexStr = HexUtil.encodeHexStr(newData).toUpperCase(Locale.ROOT);
                                    log.debug("[jSerialComm-{}]{} receive data:{}",serverName, comName, hexStr);
                                    SerialPortMessageDto serialPortMessageDto = new SerialPortMessageDto(serverName,hexStr);
                                    log.debug(serialPortMessageDto.toString());
                                    // 发布出去，具体模块再去做解析
                                    SpringContextHolder.getApplicationContext().publishEvent(serialPortMessageDto);
                                }catch (Exception e){
                                    log.debug("[jSerialComm-{}] serial port {} read data error",serverName, comName, e);
                                }
                                break;
                            case SerialPort.LISTENING_EVENT_PORT_DISCONNECTED:
                                log.info("[jSerialComm-{}] serial port {} disconnected",serverName, comName);
                                if (comPort.closePort()) log.info("[jSerialComm-{}] serial port {} close success",serverName, comName);
                                break;
                            default:
                                break;
                        }
                    }
                });
                log.info("[jSerialComm-{}] serial port {} add listener success",serverName, comName);
            }else {
                log.info("[jSerialComm-{}] serial port {} not found",serverName, comName);
            }
        }catch (Exception e){
            log.error("[jSerialComm-{}] serial port {} bind failed",serverName, comName, e);
        }
    }

    public void close() {
        if (serverSerialPort != null && serverSerialPort.isOpen()) {
            serverSerialPort.closePort();
        }
    }

    public String getComName() {
        return comName;
    }

    public void sendHexStr(String hexStr){
        serialPort.writeBytes(HexUtil.decodeHex(hexStr), hexStr.length());
    }


}
